using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;


namespace LightCAD.Three
{
    public class Path : CurvePath<Vector2>
    {
        #region Properties

        public Vector2 currentPoint;

        #endregion

        #region constructor
        public Path(ListEx<Vector2> points = null)
        {
            this.type = "Path";
            this.currentPoint = new Vector2();
            if (points != null)
            {
                this.setFromPoints(points);
            }
        }
        #endregion

        #region methods
        public Path setFromPoints(ListEx<Vector2> points)
        {
            this.moveTo(points[0].X, points[0].Y);
            for (int i = 1, l = points.Length; i < l; i++)
            {
                this.lineTo(points[i].X, points[i].Y);
            }
            return this;
        }
        public Path moveTo(double x, double y)
        {
            this.currentPoint.Set(x, y); // TODO consider referencing vectors instead of copying?
            return this;
        }
        public Path lineTo(double x, double y)
        {
            var curve = new LineCurve(this.currentPoint.Clone(), new Vector2(x, y));
            this.curves.Push(curve);
            this.currentPoint.Set(x, y);
            return this;
        }
        public Path quadraticCurveTo(double aCPx, double aCPy, double aX, double aY)
        {
            var curve = new QuadraticBezierCurve(
                this.currentPoint.Clone(),
                new Vector2(aCPx, aCPy),
                new Vector2(aX, aY)
            );
            this.curves.Push(curve);
            this.currentPoint.Set(aX, aY);
            return this;
        }
        public Path bezierCurveTo(double aCP1x, double aCP1y, double aCP2x, double aCP2y, double aX, double aY)
        {
            var curve = new CubicBezierCurve(
                this.currentPoint.Clone(),
                new Vector2(aCP1x, aCP1y),
                new Vector2(aCP2x, aCP2y),
                new Vector2(aX, aY)
            );
            this.curves.Push(curve);
            this.currentPoint.Set(aX, aY);
            return this;
        }
        public Path splineThru(ListEx<Vector2> pts)
        {
            var npts = new ListEx<Vector2> { this.currentPoint.Clone() }.Concat(pts);
            var curve = new SplineCurve(npts);
            this.curves.Push(curve);
            this.currentPoint.Copy(pts[pts.Length - 1]);
            return this;
        }
        public Path arc(double aX, double aY, double aRadius, double aStartAngle, double aEndAngle, bool aClockwise)
        {
            var x0 = this.currentPoint.X;
            var y0 = this.currentPoint.Y;
            this.absarc(aX + x0, aY + y0, aRadius,
                aStartAngle, aEndAngle, aClockwise);
            return this;
        }
        public Path absarc(double aX, double aY, double aRadius, double aStartAngle, double aEndAngle, bool aClockwise = false)
        {
            this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
            return this;
        }
        public Path ellipse(double aX, double aY, double xRadius, double yRadius, double aStartAngle, double aEndAngle, bool aClockwise, double aRotation)
        {
            var x0 = this.currentPoint.X;
            var y0 = this.currentPoint.Y;
            this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);
            return this;
        }
        public Path absellipse(double aX, double aY, double xRadius, double yRadius, double aStartAngle, double aEndAngle, bool aClockwise, double aRotation = 0)
        {
            var curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation);
            if (this.curves.Length > 0)
            {
                // if a previous curve is present, attempt to join
                var firstPoint = curve.getPoint(0);
                if (!firstPoint.Equals(this.currentPoint))
                {
                    this.lineTo(firstPoint.X, firstPoint.Y);
                }
            }
            this.curves.Push(curve);
            var lastPoint = curve.getPoint(1);
            this.currentPoint.Copy(lastPoint);
            return this;
        }
        public Path copy(Path source)
        {
            base.copy(source);
            this.currentPoint.Copy(source.currentPoint);
            return this;
        }
        public override Curve<Vector2> copy(Curve<Vector2> source)
        {
            return copy(source as Path);
        }
        public override Curve<Vector2> clone()
        {
            return new Path().copy(this);
        }

        #endregion

    }
}
